﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace WindowsCompatibility
{
    public class AdaptersInfoHelper
    {
        /***
         * DllImport  平台调用 (P/Invoke)  是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术
         * https://docs.microsoft.com/zh-cn/dotnet/standard/native-interop/pinvoke
         * 
         * https://www.pinvoke.net/
         * 
         * */

        [DllImport("iphlpapi.dll", CharSet = CharSet.Ansi)]
        public static extern int GetAdaptersInfo(IntPtr pAdapterInfo, ref Int64 pBufOutLen);

        const int MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
        const int ERROR_BUFFER_OVERFLOW = 111;
        const int MAX_ADAPTER_NAME_LENGTH = 256;
        const int MAX_ADAPTER_ADDRESS_LENGTH = 8;
        const int MIB_IF_TYPE_OTHER = 1;
        const int MIB_IF_TYPE_ETHERNET = 6;
        const int MIB_IF_TYPE_TOKENRING = 9;
        const int MIB_IF_TYPE_FDDI = 15;
        const int MIB_IF_TYPE_PPP = 23;
        const int MIB_IF_TYPE_LOOPBACK = 24;
        const int MIB_IF_TYPE_SLIP = 28;

        public List<IpAdapterInfo> GetAdapters()
        {
            var list = new List<IpAdapterInfo>();

            long structSize = Marshal.SizeOf(typeof(IP_ADAPTER_INFO));
            IntPtr pArray = Marshal.AllocHGlobal(new IntPtr(structSize));

            int ret = GetAdaptersInfo(pArray, ref structSize);

            if (ret == ERROR_BUFFER_OVERFLOW) // ERROR_BUFFER_OVERFLOW == 111
            {
                // Buffer was too small, reallocate the correct size for the buffer.
                pArray = Marshal.ReAllocHGlobal(pArray, new IntPtr(structSize));

                ret = GetAdaptersInfo(pArray, ref structSize);
            } // if

            if (ret == 0)
            {
                // Call Succeeded
                IntPtr pEntry = pArray;

                do
                {
                    // Retrieve the adapter info from the memory address
                    IP_ADAPTER_INFO entry = (IP_ADAPTER_INFO)Marshal.PtrToStructure(pEntry, typeof(IP_ADAPTER_INFO));

                    // Adapter Type
                    string tmpString = string.Empty;
                    switch (entry.Type)
                    {
                        case MIB_IF_TYPE_ETHERNET: tmpString = "Ethernet"; break;
                        case MIB_IF_TYPE_TOKENRING: tmpString = "Token Ring"; break;
                        case MIB_IF_TYPE_FDDI: tmpString = "FDDI"; break;
                        case MIB_IF_TYPE_PPP: tmpString = "PPP"; break;
                        case MIB_IF_TYPE_LOOPBACK: tmpString = "Loopback"; break;
                        case MIB_IF_TYPE_SLIP: tmpString = "Slip"; break;
                        default: tmpString = "Other/Unknown"; break;
                    } // switch

                    list.Add( new IpAdapterInfo { 
                        Index=entry.Index,
                        Type=entry.Type,
                        TypeName=tmpString,
                        AdapterName= entry.AdapterName,
                        AdapterDescription=entry.AdapterDescription,
                        DhcpEnabled=entry.DhcpEnabled==1,
                        DhcpServer=entry.DhcpServer.IpAddress.Address,
                        LeaseObtained= new DateTime(1970, 1, 1).AddSeconds(entry.LeaseObtained).ToLocalTime(),
                        LeaseExpires = new DateTime(1970, 1, 1).AddSeconds(entry.LeaseExpires).ToLocalTime(),
                        IpAddress= entry.IpAddressList.IpAddress.Address,
                        IpMask= entry.IpAddressList.IpMask.Address,
                        Gateway= entry.GatewayList.IpAddress.Address,
                        MACAddress=entry.Address,
                        HaveWins=entry.HaveWins,
                        PrimaryWinsServer=new IpAddr { 
                            IpAddress= entry.PrimaryWinsServer.IpAddress.Address,
                            IpMask= entry.PrimaryWinsServer.IpMask.Address
                        },
                        SecondaryWinsServer = new IpAddr
                        {
                            IpAddress = entry.SecondaryWinsServer.IpAddress.Address,
                            IpMask = entry.SecondaryWinsServer.IpMask.Address
                        }
                    });


                    //Console.WriteLine("Adapter Type: {0}", tmpString);

                    //Console.WriteLine("Name: {0}", entry.AdapterName);
                    //Console.WriteLine("Desc: {0}\n", entry.AdapterDescription);

                    //Console.WriteLine("DHCP Enabled: {0}", (entry.DhcpEnabled == 1) ? "Yes" : "No");

                    //if (entry.DhcpEnabled == 1)
                    //{
                    //    Console.WriteLine("DHCP Server : {0}", entry.DhcpServer.IpAddress.Address);

                    //    // Lease Obtained (convert from "time_t" to C# DateTime)
                    //    DateTime pdatDate = new DateTime(1970, 1, 1).AddSeconds(entry.LeaseObtained).ToLocalTime();
                    //    Console.WriteLine("Lease Obtained: {0}", pdatDate.ToString());

                    //    // Lease Expires (convert from "time_t" to C# DateTime)
                    //    pdatDate = new DateTime(1970, 1, 1).AddSeconds(entry.LeaseExpires).ToLocalTime();
                    //    Console.WriteLine("Lease Expires : {0}\n", pdatDate.ToString());
                    //} // if DhcpEnabled

                    //Console.WriteLine("IP Address     : {0}", entry.IpAddressList.IpAddress.Address);
                    //Console.WriteLine("Subnet Mask    : {0}", entry.IpAddressList.IpMask.Address);
                    //Console.WriteLine("Default Gateway: {0}", entry.GatewayList.IpAddress.Address);

                    //// MAC Address (data is in a byte[])
                    //tmpString = string.Empty;
                    //for (int i = 0; i < entry.Address.Length - 1; i++)
                    //{
                    //    tmpString += string.Format("{0:X2}-", entry.Address[i]);
                    //}
                    //Console.WriteLine("MAC Address    : {0}{1:X2}\n", tmpString, entry.Address[entry.Address.Length - 1]);

                    //Console.WriteLine("Has WINS: {0}", entry.HaveWins ? "Yes" : "No");
                    //if (entry.HaveWins)
                    //{
                    //    Console.WriteLine("Primary WINS Server  : {0}", entry.PrimaryWinsServer.IpAddress.Address);
                    //    Console.WriteLine("Secondary WINS Server: {0}", entry.SecondaryWinsServer.IpAddress.Address);
                    //} // HaveWins

                    // Get next adapter (if any)
                    pEntry = entry.Next;

                }
                while (pEntry != IntPtr.Zero);

                Marshal.FreeHGlobal(pArray);

            } // if
            else
            {
                Marshal.FreeHGlobal(pArray);
                throw new InvalidOperationException("GetAdaptersInfo failed: " + ret);
            }
            return list;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct IP_ADAPTER_INFO
        {
            public IntPtr Next;
            public Int32 ComboIndex;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_NAME_LENGTH + 4)]
            public string AdapterName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_DESCRIPTION_LENGTH + 4)]
            public string AdapterDescription;
            public UInt32 AddressLength;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ADAPTER_ADDRESS_LENGTH)]
            public byte[] Address;
            public Int32 Index;
            public UInt32 Type;
            public UInt32 DhcpEnabled;
            public IntPtr CurrentIpAddress;
            public IP_ADDR_STRING IpAddressList;
            public IP_ADDR_STRING GatewayList;
            public IP_ADDR_STRING DhcpServer;
            public bool HaveWins;
            public IP_ADDR_STRING PrimaryWinsServer;
            public IP_ADDR_STRING SecondaryWinsServer;
            public Int32 LeaseObtained;
            public Int32 LeaseExpires;
        }
    }

    /// <summary>
    /// IP适配器信息
    /// </summary>
    public struct IpAdapterInfo
    {
        /// <summary>
        /// 适配器Index
        /// </summary>
        public int Index { get; set; }
        /// <summary>
        /// 适配器名称
        /// </summary>
        public string AdapterName { get; set; }
        /// <summary>
        /// 适配器描述
        /// </summary>
        public string AdapterDescription { get; set; }
        /// <summary>
        /// MAC 地址
        /// </summary>
        public byte[] MACAddress { get; set; }
        /// <summary>
        /// 适配器类型
        /// </summary>
        public uint Type { get; set; }
        /// <summary>
        /// 适配器类型名称
        /// </summary>
        public string TypeName { get; set; }
        /// <summary>
        /// 启用DHCP
        /// </summary>
        public bool DhcpEnabled { get; set; }
       /// <summary>
       /// IP地址
       /// </summary>
        public string IpAddress { get; set; }
        /// <summary>
        /// 子掩码
        /// </summary>
        public string IpMask { get; set; }
        /// <summary>
        /// 网关
        /// </summary>
        public string Gateway{ get; set; }
        /// <summary>
        /// DHCP服务地址
        /// </summary>
        public string DhcpServer { get; set; }
        /// <summary>
        /// HaveWins
        /// </summary>
        public bool HaveWins { get; set; }
        /// <summary>
        /// 主要WINS服务
        /// </summary>
        public IpAddr PrimaryWinsServer { get; set; }
        /// <summary>
        /// 次要WINS服务
        /// </summary>
        public IpAddr SecondaryWinsServer { get; set; }
        /// <summary>
        /// 获得租约的时间
        /// </summary>
        public DateTime LeaseObtained { get; set; }
        /// <summary>
        /// 租约过期的时间
        /// </summary>
        public DateTime LeaseExpires { get; set; }
    }

    /// <summary>
    /// ip地址
    /// </summary>
    public struct IpAddr
    {
        /// <summary>
        /// IP地址
        /// </summary>
        public string IpAddress { get; set; }
        /// <summary>
        /// 子掩码
        /// </summary>
        public string IpMask { get; set; }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct IP_ADDR_STRING
    {
        public IntPtr Next;
        public IP_ADDRESS_STRING IpAddress;
        public IP_ADDRESS_STRING IpMask;
        public Int32 Context;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct IP_ADDRESS_STRING
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
        public string Address;
    }
}
